#!/usr/bin/env python3
import os
from pydoc import source_synopsis
import sys
import shutil
import subprocess
import argparse
from pathlib import Path
from typing import List, Set
import re

# 配置部分
MAX_ATTEMPTS = 360

def initialize_directories(rpmbuild_dir: Path, compile_order_file: Path) -> None:
    """初始化必要的目录结构"""
    
    spec_dir = rpmbuild_dir / "SPECS"
    if not spec_dir.is_dir():
        print(f"错误：未找到spec目录 {spec_dir}")
        sys.exit(1)
    
    if not compile_order_file.is_file():
        print(f"错误：未找到编译顺序文件 {compile_order_file}")
        sys.exit(1)


def build_rpm(spec_path: Path, rpmbuild_dir: Path) -> list:
    """使用rpmbuild命令构建RPM包，并返回x86_64架构二进制包名列表（包含开发包）"""
    try:
        result = subprocess.run(
            ["rpmbuild", "-ba", "--define", f"_topdir {rpmbuild_dir}", "--define", "debug_package %{nil}", "--define", "ros_distro loong", str(spec_path)],
            check=True,
            capture_output=True,
            text=True,
            timeout=10800  # 3小时超时(单位:秒)
        )
        # 解析输出获取x86_64架构二进制包名列表（包含开发包）
        packages = []
        for line in result.stdout.splitlines():
            if "Wrote: " in line and ".x86_64.rpm" in line:
                # 使用正则表达式提取包名
                match = re.search(r"Wrote: .*/(.*)\.x86_64\.rpm", line)
                if match:
                    candidate_name = match.group(1)
                    packages.append(candidate_name + ".x86_64.rpm")
                    print(f"✅编译成功：{spec_path.name}，包名：{candidate_name}")
        return packages
    except subprocess.CalledProcessError as e:
        print(f"❌编译失败：{spec_path}")
        print(f"错误输出：{e.stderr}")
        return None
    except subprocess.TimeoutExpired:
        print(f"❌编译超时：{spec_path}，超过3小时未完成")
        return None

def install_rpm_packages(rpm_dir:Path, packages: list) -> bool:
    """按顺序安装RPM包"""
    print(f"准备安装的包：{packages}")
    if not rpm_dir.is_dir():
        print("RPM 目录不存在，跳过安装步骤。")
        return False
    
    # 检查rpm_dir下是否有packages列表中的rpm文件
    rpm_files = []
    for package in packages:
        rpm_files.extend(list(rpm_dir.glob(f"{package}")))

    # 去除重复包,并且排序
    rpm_files = list(set(rpm_files))
    rpm_files.sort()

    print(f"找到 {len(rpm_files)} 个RPM文件：{rpm_files}")

    for rpm_path in rpm_files:
        # 检查文件是否存在
        if not rpm_path.is_file():
            print(f"错误：RPM 文件 {rpm_path} 不存在")
            return False
        
        # 获取包名
        try:
            pkg_name = subprocess.check_output(
                ["rpm", "-qp", "--queryformat", "%{NAME}", str(rpm_path)],
                text=True
            ).strip()
            print(f"查询到包名：{pkg_name} ")
        except subprocess.CalledProcessError:
            print(f"无法解析包名: {rpm_path}")
            return False
        
        # 检查是否已安装
        try:
            subprocess.check_output(["rpm", "-q", pkg_name], stderr=subprocess.STDOUT)
            print(f"Package '{pkg_name}' 已安装，跳过。")
        except subprocess.CalledProcessError:
            # 未安装，执行安装
            print(f"正在安装 {rpm_path} ...")
            try:
                subprocess.run(
                    ["rpm", "-ivh", str(rpm_path)],
                    check=True,
                    text=True
                )
                print(f"✅安装成功：{pkg_name}！")
            except subprocess.CalledProcessError as e:
                print(f"❌安装失败：{pkg_name}")
                print(f"错误输出：{e.stderr}")
                return False
    return True


def parse_build_status(build_status_file: Path) -> List[str]:
    """解析buildstatus文件，获取三类spec文件"""

    # 已经成功处理的spec样例 “ament-package.spec ###success###”
    # 处理失败的spec样例 “ament-package.spec ###build failure###” 或 “ament-package.spec ###install failure###”
    # 未处理的spec样例 “ament-package.spec”

    success_specs = []
    failure_specs = []
    unprocessed_specs = []

    with build_status_file.open('r') as f:
        lines = [line.strip() for line in f]
    
    success_specs = [line.split(' ###success###')[0] for line in lines if ' ###success###' in line]
    build_failure_specs = [line.split(' ###')[0] for line in lines if ' ###build failure###' in line]
    install_failure_specs = [line.split(' ###')[0] for line in lines if ' ###install failure###' in line]
    unprocessed_specs = [line.split(' ###')[0] for line in lines if ' ###success###' not in line and ' ###build failure###' not in line and ' ###install failure###' not in line]
    return success_specs, build_failure_specs, install_failure_specs, unprocessed_specs

def update_build_status(build_status_file: Path, spec_name: str, status: str) -> None:
    """找到buildstatus文件中spec_name所在行，更新状态"""
    """更新构建状态文件中的包状态"""
    try:
        # 读取所有行
        with build_status_file.open('r') as f:
            lines = f.readlines()
        
        # 查找并替换匹配行
        found = False
        for i, line in enumerate(lines):
            if line.startswith(spec_name):
                lines[i] = f"{spec_name} ###{status}###\n"
                found = True
                break
        
        # 如果未找到匹配行，追加新行
        if not found:
            lines.append(f"{spec_name} ###{status}###\n")
        
        # 写入更新后的内容
        with build_status_file.open('w') as f:
            f.writelines(lines)
            
        return True
        
    except Exception as e:
        print(f"❌更新状态文件失败: {e}")
        return False


def read_compile_order(compile_order_file: Path) -> List[str]:
    """读取编译顺序文件中的所有spec名称"""
    try:
        with compile_order_file.open('r') as f:
            return [line.strip() for line in f if line.strip()]
    except Exception as e:
        print(f"读取编译顺序文件失败: {e}")
        sys.exit(1)


def main() -> None:
    # 解析命令行参数
    parser = argparse.ArgumentParser(description='Build and install RPM packages.')
    parser.add_argument('--rpmbuild-dir', default=Path(os.getcwd()) / 'build', type=Path, 
                        help='RPM build directory (default: current directory/build)')
    parser.add_argument('--download-dir', default=Path(os.getcwd()) / 'downloads', type=Path, 
                        help='download directory (default: current directory/downloads)')
    parser.add_argument('--compile-order-file', default=Path(os.getcwd()) / 'orderedspec.conf', type=Path,
                        help='Compile order file (default: current directory/orderedspec.conf)')
    parser.add_argument('--build-status-file', default=Path(os.getcwd()) / 'buildstatus', type=Path,
                        help='build status file (default: current directory/buildstatus)')
    parser.add_argument('--all', action='store_true', default=False, help='全量模式编译所有包，忽略之前的构建状态')
    parser.add_argument('--skip', action='store_true', default=False, help='增量编译状态下，默认处理所有的失败和未处理包')
    args = parser.parse_args()
    
    initialize_directories(args.rpmbuild_dir, args.compile_order_file)
    
    # 安装rpmbuild命令
    if shutil.which('rpmbuild') is None:
        print("⚠️ rpmbuild 未安装，正在安装...")
        try:
            subprocess.run(['yum', 'install', '-y', 'rpm-build'], check=True)
            print("✅ rpmbuild 安装成功")
        except subprocess.CalledProcessError as e:
            print("❌ rpmbuild 安装失败:", e)
            sys.exit(1)
    
    rpmbuild_dir = args.rpmbuild_dir
    download_dir = args.download_dir
    build_status_file = args.build_status_file
    compile_order_file = args.compile_order_file
    spec_dir = rpmbuild_dir / "SPECS"
    processed_files: Set[str] = set()  

    # 准备buildstatus文件，内容复制compileorder文件
    if args.all or not build_status_file.exists():
        shutil.copy(compile_order_file, build_status_file)

    # 读取buildstatus文件内容，获取三类spec文件
    success_specs, build_failure_specs, install_failure_specs, unprocessed_specs = parse_build_status(build_status_file)

    # 根据全量模式参数决定目标spec列表
    if args.all:
        print("全量模式：将编译所有spec文件...")
        target_spec_list = read_compile_order(compile_order_file)
    else:
        if args.skip:
            print("增量模式：将编译所有失败和未处理的spec文件...")
            target_spec_list = build_failure_specs + install_failure_specs + unprocessed_specs
        else:
            print("增量模式：将编译所有未处理的spec文件...")
            target_spec_list = unprocessed_specs

    # 本次编译前统计，总数以及三类spec数量分行打印
    print(f"\n本次编译前：共 {len(success_specs) + len(build_failure_specs) + len(install_failure_specs) + len(unprocessed_specs)} 个.spec 文件。")
    if not args.all:
        print(f"成功 {len(success_specs)} 个.spec 文件。")
        print(f"失败 {len(build_failure_specs) + len(install_failure_specs)} 个.spec 文件。")
        print(f"    编译失败 {len(build_failure_specs)} 个.spec 文件。")
        print(f"    安装失败 {len(install_failure_specs)} 个.spec 文件。")
        print(f"未处理 {len(unprocessed_specs)} 个.spec 文件。\n")
    else:
        print(f"全量模式下，将重新编译所有 {len(target_spec_list)} 个.spec 文件。\n")

    # 先切换到args.rpmbuild_dir目录
    try:
        os.chdir(rpmbuild_dir)
    except Exception as e:
        print(f"❌切换目录失败：{e}")
        sys.exit(1)

    # 全量模式下卸载已经安装的agiros包
    if args.all:
        print("全量模式：正在卸载所有agiros开头的RPM包...")
        try:
            # 获取所有aigros开头的包
            result = subprocess.run(['rpm', '-qa', '--queryformat', '%{NAME}\n'], capture_output=True, text=True, check=True)
            agiros_packages = [pkg for pkg in result.stdout.splitlines() if pkg.startswith('agiros')]
            if agiros_packages:
                # 卸载所有找到的包
                subprocess.run(['rpm', '-e'] + agiros_packages, check=True)
                print(f"成功卸载 {len(agiros_packages)} 个agiros包")
            else:
                print("未找到agiros开头的包，无需卸载")
        except subprocess.CalledProcessError as e:
            print(f"卸载aigros包失败: {e.stderr}")
            sys.exit(1)

    print(f"开始本次编译&安装...")

    for spec_name in target_spec_list:
        spec_path = spec_dir / spec_name
        
        print(f"========正在处理 {spec_path}========")

        # 如果文件不存在，终止
        if not spec_path.exists():
            print(f"编译目录中未找到文件 {spec_name} ，编译终止。")
            continue

        # 编译前从downloads目录重新拷贝对应的补丁文件到rpmbuild_dir/SOURCES，规避同名patch问题
        package_name = spec_name.replace('.spec', '')
        if package_name == 'agiros-base':
            package_name = 'ros-base'
        package_path = download_dir / package_name
        if not package_path.exists():
            print(f"downloads原始包目录 {package_path} 未找到，编译终止。")
            continue

        patch_files = set()
        for patch in package_path.glob("*.patch"):
            patch_files.add(patch)
        patch_files = sorted(patch_files)

        print("开始复制同级.patch文件...")
        for file in patch_files:
            try:
                shutil.copy2(file, rpmbuild_dir / "SOURCES")
            except Exception as e:
                print(f"复制失败 {file}: {e}")

        # 编译成功，直接安装
        packages = build_rpm(spec_path, rpmbuild_dir)
        if packages:
            if install_rpm_packages(rpmbuild_dir / "RPMS" / "x86_64", packages):

                # 安装成功，更新buildstatus文件
                update_build_status(build_status_file, spec_name, 'success')
            else:
                # 安装失败，更新buildstatus文件
                update_build_status(build_status_file, spec_name, 'install failure')
                #break
        else:
            # 编译失败，更新buildstatus文件
            update_build_status(build_status_file, spec_name, 'build failure')

            print(f"文件 {spec_name} 编译失败，编译终止。")
            #break
    print(f"=======================================")
    print(f"本次编译&安装结束")

    # 本次编译后统计，总数以及三类spec数量分行打印
    success_specs, build_failure_specs, install_failure_specs, unprocessed_specs = parse_build_status(build_status_file)
    print(f"\n本次编译&安装后：共 {len(success_specs) + len(build_failure_specs) + len(install_failure_specs) + len(unprocessed_specs)} 个.spec 文件。")
    print(f"成功 {len(success_specs)} 个.spec 文件。")
    print(f"失败 {len(build_failure_specs) + len(install_failure_specs)} 个.spec 文件。")
    print(f"    编译失败 {len(build_failure_specs)} 个.spec 文件。")
    print(f"    安装失败 {len(install_failure_specs)} 个.spec 文件。")
    print(f"未处理 {len(unprocessed_specs)} 个.spec 文件。\n")


if __name__ == "__main__":
    main()